package com.jmx.mstcodesandbox.utils;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.StatsCmd;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.Statistics;
import com.github.dockerjava.api.model.StreamType;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.jmx.mstcodesandbox.model.ProcessInfo;
import org.springframework.util.StopWatch;

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import static com.jmx.mstcodesandbox.javasandbox.JavaNativeCodeSandBox.EXEC_TIME_OUT;

/**
 * 运行cmd命令行工具类
 */
public class DockerExecUtil {
    public static ProcessInfo executeCmd(DockerClient dockerClient,String containerId,String[] cmdArray){
        ProcessInfo processInfo = new ProcessInfo();
        final String[] message = new String[1];
        final Integer[] status = new Integer[1];
        final Boolean[] isCompleted = new Boolean[1];
        isCompleted[0] = false;
        StopWatch stopWatch = new StopWatch();
        ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
                .withCmd(cmdArray)
                .withAttachStderr(true)
                .withAttachStdin(true)
                .withAttachStdout(true)
                .exec();
        System.out.println("创建执行指令: " + execCreateCmdResponse);
        String execId = execCreateCmdResponse.getId();
        ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
            @Override
            public void onNext(Frame frame) {
                StreamType streamType = frame.getStreamType();
                message[0] = new String(frame.getPayload());
                if (StreamType.STDERR.equals(streamType)) {
                    status[0] = 3;
                    System.out.println("输出错误：" + message[0]);
                } else {
                    status[0] = 0;
                    System.out.println("输出结果：" + message[0]);
                }
                processInfo.setCode(status[0]);
                message[0] = message[0].replaceAll("\\n$", "");
                processInfo.setMessage(message[0]);
                super.onNext(frame);
            }

            @Override
            public void onComplete() {
                isCompleted[0] = true;
                super.onComplete();
            }
        };
        try {
            StatsCmd statsCmd = dockerClient.statsCmd(containerId);
            ResultCallback<Statistics> resultCallback = new ResultCallback<Statistics>() {
                @Override
                public void onNext(Statistics statistics) {
                   // System.out.println("内存占用 = " + statistics.getMemoryStats().getMaxUsage());
                    Long memoryUsage = statistics.getMemoryStats().getMaxUsage();
                    if (memoryUsage != null) {
                        processInfo.setMemory(memoryUsage/1024);
                    }
                }
                @Override
                public void onStart(Closeable closeable) {

                }

                @Override
                public void onError(Throwable throwable) {

                }

                @Override
                public void onComplete() {

                }

                @Override
                public void close() throws IOException {

                }
            };
            statsCmd.exec(resultCallback);
            //执行用户代码
            stopWatch.start();
            dockerClient.execStartCmd(execId)
                    .exec(execStartResultCallback)
                    .awaitCompletion(EXEC_TIME_OUT, TimeUnit.MILLISECONDS);
            stopWatch.stop();
            resultCallback.close();
            long time = 0L;
            if(isCompleted[0]){
                time = stopWatch.getLastTaskTimeMillis();
            }else {
                time = EXEC_TIME_OUT;
                processInfo.setMessage("程序执行超时");
            }
            processInfo.setTime(time);
        } catch (InterruptedException | IOException e) {
            throw new RuntimeException(e);
        }
        return processInfo;
    }
}
